#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lvgl.h"
#include "esp_log.h"
#include "st7789_driver.h"
#include "cst816t_driver.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "PipeLine.h"

#define TAG     "lv_port"

#define LCD_WIDTH       240
#define LCD_HEIGHT      280

static lv_disp_drv_t    disp_drv;   // 定义一个显示驱动结构体
static bool is_display_off = false; // 添加一个变量来记录屏幕是否关闭
static bool is_sleep_mode = false;  // 添加一个变量来记录屏幕是否处于休眠模式
static int last_activity_time = 0;  // 定义全局变量，记录最后操作时间

void lv_flush_done_cb(void* param)
{
    lv_disp_flush_ready(&disp_drv); //告诉 LVGL：这一帧已经发完了，可以继续画下一帧。
}

void disp_flush(struct _lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    st7789_flush(area->x1,area->x2 + 1,area->y1+20,area->y2+20 + 1,color_p); //调用驱动的API，实时更新显示
}

void lv_disp_init(void)
{
    // 定义一个静态的显示缓冲区
    static lv_disp_draw_buf_t   disp_buf;
    // 计算显示缓冲区的大小
    const size_t disp_buf_size = LCD_WIDTH*(LCD_HEIGHT/10);

    // 分配两个缓冲区，大小为disp_buf_size*sizeof(lv_color_t)，并要求内存位于内部且支持DMA操作
    lv_color_t *disp1 = heap_caps_malloc(disp_buf_size*sizeof(lv_color_t),MALLOC_CAP_SPIRAM|MALLOC_CAP_DMA);
    lv_color_t *disp2 = heap_caps_malloc(disp_buf_size*sizeof(lv_color_t),MALLOC_CAP_SPIRAM|MALLOC_CAP_DMA);

    // 检查内存分配是否成功，如果disp1或disp2为NULL，则内存分配失败
    if(!disp1 || !disp2)
    {
        ESP_LOGE(TAG,"disp buff malloc fail!");
        return;
    }

    // 初始化显示缓冲区结构体，将两个分配的缓冲区地址传入
    lv_disp_draw_buf_init(&disp_buf,disp1,disp2,disp_buf_size);
    // 定义一个显示驱动结构体
    lv_disp_drv_init(&disp_drv);

    // 设置显示驱动的水平分辨率和垂直分辨率
    disp_drv.hor_res = LCD_WIDTH;
    disp_drv.ver_res = LCD_HEIGHT;
    // 设置显示驱动使用的缓冲区
    disp_drv.draw_buf = &disp_buf;
    disp_drv.flush_cb = disp_flush;
    // 注册显示驱动
    lv_disp_drv_register(&disp_drv);
}

// 定时器回调函数
void IRAM_ATTR indev_read(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    int16_t x,y;
    int state;
    cst816t_read(&x,&y,&state);
    data->point.x = x;
    data->point.y = y;
    data->state = state;
    // 如果是触摸状态
    if (state == 1) 
    {  
        last_activity_time = lv_tick_get();  // 更新最后操作时间
        st7789_lcd_backlight(true);  // 开启背光
    }
}

void lv_indev_init(void)
{
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = indev_read;
    lv_indev_drv_register(&indev_drv);
}

void st7789_hw_init(void)
{
    st7789_cfg_t st7789_config = {
        .bl = GPIO_NUM_15,
        .clk = GPIO_NUM_6,
        .cs = GPIO_NUM_5,
        .dc = GPIO_NUM_4,
        .mosi = GPIO_NUM_7,
        .rst = GPIO_NUM_8,
        .spi_fre = 40*1000*1000,
        .height = LCD_HEIGHT,
        .width = LCD_WIDTH,
        .spin = 0,
        .done_cb = lv_flush_done_cb,
        .cb_param = &disp_drv,
    };

    st7789_driver_hw_init(&st7789_config);
}

void cst816t_hw_init(void)
{
    cst816t_cfg_t cst816t_config = 
    {
        .scl = GPIO_NUM_10,
        .sda = GPIO_NUM_11,
        .fre = 300*1000,
        .x_limit = LCD_WIDTH,
        .y_limit = LCD_HEIGHT,
    };
    cst816t_init(&cst816t_config);
}

void lv_timer_cb(void* arg)
{
    uint32_t tick_interval = *((uint32_t*)arg);
    lv_tick_inc(tick_interval); //每tick_interval产生一次tick，就是告诉LVGL一个TICK为五毫秒。
}

void lv_tick_init(void)
{
    // 定义一个静态的32位无符号整数变量tick_interval，用于设置定时器的间隔时间，单位为毫秒
    static uint32_t tick_interval = 5;
    
    // 定义一个esp_timer_create_args_t类型的常量结构体arg，用于存储定时器的配置参数
    const esp_timer_create_args_t arg = 
    {
        // 将tick_interval的地址传递给定时器回调函数，以便回调函数可以访问和使用此变量
        .arg = &tick_interval,
        // 指定定时器回调函数的指针，当定时器触发时，会调用此函数
        .callback = lv_timer_cb,
        // 定时器名称，这里为空字符串
        .name = "",
        // 定义定时器的分派方法为ESP_TIMER_TASK，表示定时器事件将被发送到一个特定的任务队列（表示不在中断中处理，作为一个独立的任务进行处理）
        .dispatch_method = ESP_TIMER_TASK,
        // 设置skip_unhandled_events为true，这意味着如果定时器事件被跳过（例如由于系统负载过高），将不会在事件恢复后重新处理这些被跳过的事件
        .skip_unhandled_events = true,
    };

    // 声明一个esp_timer_handle_t类型的变量timer_handle，用于存储定时器句柄
    esp_timer_handle_t timer_handle;
    
    // 创建定时器，根据arg中的配置参数进行初始化，并将定时器句柄存储在timer_handle中
    esp_timer_create(&arg, &timer_handle);
    
    // 启动定时器，设置为周期性触发，周期为tick_interval * 1000微秒（即tick_interval毫秒）
    esp_timer_start_periodic(timer_handle, tick_interval * 1000);
}

void backlight_control_task(void *pvParameters) 
{
    xEventGroupWaitBits(xEventGroup, NTP_DONE_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
    
    vTaskDelay(pdMS_TO_TICKS(2000));

    while (1) 
    {
        int current_time = lv_tick_get();  // 获取当前时间刻度
        uint32_t elapsed_time = current_time - last_activity_time;

        // 定义两个时间阈值
        const uint32_t DISPLAY_OFF_TIMEOUT = 10000;  // 10秒无操作后关闭显示
        const uint32_t SLEEP_MODE_TIMEOUT = 30000;   // 30秒无操作后进入休眠模式

        if (elapsed_time > SLEEP_MODE_TIMEOUT) 
        {
            // 超过 30 秒无操作，进入休眠模式
            if (!is_sleep_mode) {
                st7789_lcd_backlight(false);
                st7789_turn_off_display();
                st7789_enter_sleep_mode();
                is_display_off = true;
                is_sleep_mode = true;
            }
        } 
        else if (elapsed_time > DISPLAY_OFF_TIMEOUT) 
        {
            // 超过 10 秒无操作，关闭显示
            if (!is_display_off && !is_sleep_mode) {
                st7789_lcd_backlight(false);
                st7789_turn_off_display();
                is_display_off = true;
            }
        } 
        else 
        {
            // 用户有操作，退出休眠模式并开启显示
            if (is_sleep_mode) {
                st7789_exit_sleep_mode();
                st7789_turn_on_display();
                st7789_lcd_backlight(true);
                is_sleep_mode = false;
                is_display_off = false;
            } 
            else if (is_display_off) {
                st7789_turn_on_display();
                st7789_lcd_backlight(true);
                is_display_off = false;
            }
        }

        vTaskDelay(pdMS_TO_TICKS(200));  // 每隔一段时间检查一次
    }
}

void lv_port_init(void)
{
    lv_init();                // 初始化 LVGL 库
    st7789_hw_init();         // 初始化 ST7789 显示硬件
    cst816t_hw_init();        // 初始化 CST816T 触摸硬件
    lv_disp_init();           // 初始化 LVGL 显示驱动
    lv_indev_init();          // 初始化 LVGL 输入设备驱动
    lv_tick_init();           // 初始化 LVGL 的时钟系统
}


